package com.ctshk.rpc.hotel.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ctshk.common.constant.ElasticSearchConstant;
import com.ctshk.common.constant.RedisConstants;
import com.ctshk.common.dto.Result;
import com.ctshk.common.enums.*;
import com.ctshk.common.enums.Currency;
import com.ctshk.common.exception.BusinessException;
import com.ctshk.common.model.PageReq;
import com.ctshk.common.model.PageResponse;
import com.ctshk.common.utils.EntityUtil;
import com.ctshk.common.utils.redis.KeyUtil;
import com.ctshk.rpc.cms.dto.BannerDTO;
import com.ctshk.rpc.cms.dto.NoticeDTO;
import com.ctshk.rpc.cms.req.BannerPositionByTypeReq;
import com.ctshk.rpc.cms.service.IBannerService;
import com.ctshk.rpc.cms.service.INoticeService;
import com.ctshk.rpc.hotel.config.ElasticSearchConfiguration;
import com.ctshk.rpc.hotel.dto.HotelQueryListDTO;
import com.ctshk.rpc.hotel.dto.booking.HotelBookingDetailsDTO;
import com.ctshk.rpc.hotel.dto.h5.*;
import com.ctshk.rpc.hotel.dto.pc.HotelHotCityDTO;
import com.ctshk.rpc.hotel.dto.pc.HotelHotsPcDTO;
import com.ctshk.rpc.hotel.entity.*;
import com.ctshk.rpc.hotel.entity.es.*;
import com.ctshk.rpc.hotel.mapper.*;
import com.ctshk.rpc.hotel.mapper.es.*;
import com.ctshk.rpc.hotel.req.cache.HotelCacheOpsReq;
import com.ctshk.rpc.hotel.req.h5.HotelDetailsQueryH5Req;
import com.ctshk.rpc.hotel.req.h5.HotelQueryReq;
import com.ctshk.rpc.hotel.req.h5.RoomDetailsQueryH5Req;
import com.ctshk.rpc.hotel.service.IHotelH5Service;
import com.ctshk.rpc.hotel.service.IHotelService;
import com.ctshk.rpc.user.req.UserFootPointReq;
import com.ctshk.rpc.user.service.IUserFootPointService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @description 酒店资源 服务实现类 - C端-H5
 * @author 谢诗宏
 * @date 2021/5/20
 */
@DubboService
public class HotelH5ServiceImpl extends ServiceImpl<HotelMapper, Hotel> implements IHotelH5Service {

    @Autowired
    private HotelMapper hotelMapper;
    @Autowired
    private HotelRoomTypeMapper hotelRoomTypeMapper;
    @Autowired
    private HotelProductMapper hotelProductMapper;
    @Autowired
    private HotelProductCostMapper hotelProductCostMapper;
    @Autowired
    private HotelProductCostDateMapper hotelProductCostDateMapper;
    @Autowired
    private HotelEsMapper hotelEsMapper;
    @Autowired
    private HotelRoomEsMapper hotelRoomEsMapper;
    @Autowired
    private HotelProductEsMapper hotelProductEsMapper;
    @Autowired
    private HotelProductCostEsMapper hotelProductCostEsMapper;
    @Autowired
    private HotelProductCostDateEsMapper hotelProductCostDateEsMapper;
    @Autowired
    private HotelRecommendEsMapper hotelRecommendEsMapper;
    @Autowired
    private HotelRecommendPcEsMapper hotelRecommendPcEsMapper;
    @Autowired
    private HotelSearchEsMapper hotelSearchEsMapper;
    @Autowired
    private HotelDetailEsMapper hotelDetailEsMapper;
    @Autowired
    private HotelRoomProductDetailEsMapper hotelRoomProductDetailEsMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private IHotelService hotelService;

    @DubboReference
    private INoticeService noticeService;
    @DubboReference
    private IBannerService bannerService;
    @DubboReference
    private IUserFootPointService userFootPointService;

    public void init() {
        List<Hotel> hotels = hotelMapper.selectList(Wrappers.<Hotel>lambdaQuery().eq(Hotel::getIsDeleted, 0));
        List<HotelEs> hotelEss = EntityUtil.copyList(hotels, HotelEs.class);
        if (CollUtil.isNotEmpty(hotelEss)) {
            hotelEsMapper.saveAll(hotelEss);
        }

        List<HotelRoomType> hotelRooms = hotelRoomTypeMapper.selectList(Wrappers.<HotelRoomType>lambdaQuery().eq(HotelRoomType::getIsDeleted, 0));
        List<HotelRoomEs> hotelRoomEss = EntityUtil.copyList(hotelRooms, HotelRoomEs.class);
        if (CollUtil.isNotEmpty(hotelRoomEss)) {
            hotelRoomEsMapper.saveAll(hotelRoomEss);
        }

        List<HotelProduct> hotelProducts = hotelProductMapper.selectList(Wrappers.<HotelProduct>lambdaQuery().eq(HotelProduct::getIsDeleted, 0));
        List<HotelProductEs> hotelProductEss = EntityUtil.copyList(hotelProducts, HotelProductEs.class);
        if (CollUtil.isNotEmpty(hotelProductEss)) {
            hotelProductEsMapper.saveAll(hotelProductEss);
        }

        List<HotelProductCost> hotelProductsCosts = hotelProductCostMapper.selectList(Wrappers.query());
        List<HotelProductCostEs> hotelProductCostEss = EntityUtil.copyList(hotelProductsCosts, HotelProductCostEs.class);
        if (CollUtil.isNotEmpty(hotelProductCostEss)) {
            hotelProductCostEsMapper.saveAll(hotelProductCostEss);
        }

        List<HotelProductCostDate> hotelProductCostDates = hotelProductCostDateMapper.selectList(Wrappers.query());
        List<HotelProductCostDateEs> hotelProductCostDateEss = EntityUtil.copyList(hotelProductCostDates, HotelProductCostDateEs.class);
        if (CollUtil.isNotEmpty(hotelProductCostDateEss)) {
            hotelProductCostDateEsMapper.saveAll(hotelProductCostDateEss);
        }

    }

    @Override
    public Result initEsRecommend() {
        String now = LocalDate.now().toString();
        List<HotelQueryListDTO> hotels = hotelService.queryRecommendationListH5(now);
        List<HotelRecommendListEs> hotelEss = EntityUtil.copyList(hotels, HotelRecommendListEs.class);
        if (CollUtil.isNotEmpty(hotelEss)) {
            // 加载进es
            hotelRecommendEsMapper.saveAll(hotelEss);
        }
        return Result.success();
    }

    public Result initEsRecommendPC(List<String> citys) {
        String now = LocalDate.now().toString();
        List<HotelHotCityDTO> result = new ArrayList<>();
        for (String city : citys) {
            List<HotelQueryListDTO> hotels = hotelService.queryRecommendationListPC(now, city);
            HotelHotCityDTO hotelHotCityDTO = new HotelHotCityDTO();
            hotelHotCityDTO.setCity(city);
            hotelHotCityDTO.setHotels(EntityUtil.copyList(hotels, HotelHotDTO.class));
            result.add(hotelHotCityDTO);
        }
        List<HotelRecommendListPcEs> hotelEss = EntityUtil.copyList(result, HotelRecommendListPcEs.class);
        if (CollUtil.isNotEmpty(hotelEss)) {
            // 加载进es
            hotelRecommendPcEsMapper.saveAll(hotelEss);
        }
        return Result.success();
    }

    public Result initEsDetail(HotelDetailsQueryH5Req req) {
        Result<HotelBookingDetailsDTO> hotel = hotelService.detailsH5(req);
        if (!hotel.isSuccess()) {
            SystemError error = SystemError.getDefined(hotel.getCode());
            return Result.failed(error);
        }
        HotelDetailEs hotelEs = EntityUtil.copy(hotel.getData(), HotelDetailEs.class);
        if (null != hotelEs) {
            // 加载进es
            hotelDetailEsMapper.save(hotelEs);
        }
        return Result.success();
    }

    public Result initEsDetailRoom(RoomDetailsQueryH5Req req) {
        HotelRoomDTO room = hotelService.detailsRoomH5(req);
        HotelRoomProductEs esEntity = EntityUtil.copy(room, HotelRoomProductEs.class);
        if (null != esEntity) {
            // 加载进es
            hotelRoomProductDetailEsMapper.save(esEntity);
        }
        return Result.success();
    }

    public void delEsSearch() {
        hotelRecommendEsMapper.deleteAll();
        hotelSearchEsMapper.deleteAll();
        hotelDetailEsMapper.deleteAll();
    }

    private List<HotelHotsH5DTO> builProductData() {
        return null;
    }

    /**
     * 检索缓存初始化
     *
     * @return
     */
    @Override
    public Result cacheInit() {
        LocalDateTime now = LocalDateTime.now();
        // 检索缓存上架es
        init();
        // 标记缓存更新时间
        return Result.success();
    }

    @Override
    public void cacheUpdate(long id) {
        // 删除H5推荐列表Redis缓存
        Set<String> keysRecommend = redisTemplate.keys(KeyUtil.genLikeKey(RedisConstants.HOTEL_RECOMMEND_TIME));
        if (CollUtil.isNotEmpty(keysRecommend)) {
            redisTemplate.delete(keysRecommend);
        }
        // 删除PC推荐列表Redis缓存
        Set<String> keysRecommendPc = redisTemplate.keys(KeyUtil.genLikeKey(RedisConstants.HOTEL_RECOMMEND_PC_TIME));
        if (CollUtil.isNotEmpty(keysRecommendPc)) {
            redisTemplate.delete(keysRecommendPc);
        }
        // 删除酒店详情Redis缓存
        Set<String> keys = redisTemplate.keys(KeyUtil.genLikeKey(RedisConstants.HOTEL_DETAIL, String.valueOf(id)));
        if (CollUtil.isNotEmpty(keys)) {
            redisTemplate.delete(keys);
        }
        // 删除酒店详情Redis缓存
        Set<String> keysRoom = redisTemplate.keys(KeyUtil.genLikeKey(RedisConstants.HOTEL_DETAIL_ROOM, String.valueOf(id)));
        if (CollUtil.isNotEmpty(keysRoom)) {
            redisTemplate.delete(keysRoom);
        }

        // 删除H5推荐列表es数据
        hotelRecommendEsMapper.deleteAll();
        // 删除PC推荐列表es数据
        hotelRecommendPcEsMapper.deleteAll();
        // 删除搜索列表es数据
        hotelSearchEsMapper.deleteAll();
        // 删除酒店详情es数据
        hotelDetailEsMapper.deleteById(id);
        HotelRoomProductEs roomProductEs = new HotelRoomProductEs();
        roomProductEs.setHotelId(id);
        // 删除房间详情es数据
        hotelRoomProductDetailEsMapper.delete(roomProductEs);
    }

    @Override
    public PageResponse<HotelHotsH5DTO> queryRecommendations(PageReq req) {
        // 构建产品检索条件
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConstant.HOTEL_PRODUCT_INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery.must(QueryBuilders.termQuery("isRecommend", 1));
        sourceBuilder.collapse(new CollapseBuilder("hotelId"));
        sourceBuilder.sort("recommendTime");
        sourceBuilder.query(boolQuery);
        sourceBuilder.from((req.getPageNo() - 1) * req.getPageSize());
        sourceBuilder.size(req.getPageSize());
        searchRequest.source(sourceBuilder);

        try {
            // 解析响应结果
            SearchResponse response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
            SearchHit[] hits = response.getHits().getHits();
            HotelHotsH5DTO hotsDTOS = new HotelHotsH5DTO();
            List<HotelHotDTO> hotelHotDTOS = new ArrayList<>();
            for (SearchHit hit : hits) {

                HotelHotDTO hotelHotDTO = new HotelHotDTO();
                String str = hit.getSourceAsString();
                JSONObject hotJson = JSONObject.parseObject(str);

                // 构建酒店检索条件
                SearchRequest searchRequestHotel = new SearchRequest(ElasticSearchConstant.HOTEL_INDEX);
                SearchSourceBuilder sourceBuilderHotel = new SearchSourceBuilder();
                BoolQueryBuilder boolQueryHotel = QueryBuilders.boolQuery();
                boolQueryHotel.must(QueryBuilders.termQuery("id", hotJson.getLong("hotelId")));
                sourceBuilderHotel.query(boolQueryHotel);
                searchRequestHotel.source(sourceBuilderHotel);
                SearchResponse responseHotel = restHighLevelClient.search(searchRequestHotel, ElasticSearchConfiguration.COMMON_OPTIONS);
                SearchHit[] hitsHotel = responseHotel.getHits().getHits();
                for (SearchHit hitHotel : hitsHotel) {
                    hotelHotDTO = JSONObject.parseObject(hitHotel.getSourceAsString(), hotelHotDTO.getClass());
                }

                hotelHotDTOS.add(hotelHotDTO);
            }
            hotsDTOS.setHotels(hotelHotDTOS);
            PageResponse result = new PageResponse(Arrays.asList(hotsDTOS),
                    true,
                    true,
                    0,
                    (req.getPageNo() - 1) * req.getPageSize(),
                    req.getPageSize());
            return result;
        } catch (IOException e) {
            log.error("【业务模块APP服務-查询es产品列表】异常", e);
            throw new BusinessException(SystemError.ES_IO_EXCEPTION_19004);
        }

    }

    @Override
    public PageResponse<HotelHotsH5DTO> queryRecommendations2(PageReq req) {
        String now = LocalDate.now().toString();
        Object recommendRedis = redisTemplate.opsForValue().get(KeyUtil.genKey(RedisConstants.HOTEL_RECOMMEND_TIME, now, req.getPageNo().toString(), req.getPageSize().toString()));
        if (recommendRedis != null) {
            PageResponse<HotelHotsH5DTO> result = JSONObject.parseObject(recommendRedis.toString(), PageResponse.class);
            return result;
        }
        // 构建产品检索条件
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConstant.HOTEL_RECOMMEND_INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        sourceBuilder.query(boolQuery);
        sourceBuilder.from((req.getPageNo() - 1) * req.getPageSize());
        sourceBuilder.size(req.getPageSize());
        searchRequest.source(sourceBuilder);

        try {
            // 解析响应结果
            SearchResponse response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
            SearchHit[] hits = response.getHits().getHits();
            if (hits.length == 0) {
                initEsRecommend();
                response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
                hits = response.getHits().getHits();
            }

            /**
             * 获取通知
             */
            BannerPositionByTypeReq noticReq = new BannerPositionByTypeReq();
            noticReq.setLinkType(6);
            noticReq.setCode("APP_NOTICE");
            List<NoticeDTO> noticeDTOS = noticeService.queryListByCode(noticReq);
            List<HotelHotTipsDTO> tips = new ArrayList<>();
            Optional.ofNullable(noticeDTOS).orElse(new ArrayList<>()).forEach(dto -> {
                HotelHotTipsDTO hotTipsDTO = new HotelHotTipsDTO();
                hotTipsDTO.setTitle(dto.getTitle());
                hotTipsDTO.setContent(dto.getNoticeContent());
                tips.add(hotTipsDTO);
            });

            /**
             * 获取banner
              */
            BannerPositionByTypeReq bannerReq = new BannerPositionByTypeReq();
            bannerReq.setLinkType(6);
            bannerReq.setCode("APP_HOTEL");
            List<BannerDTO> bannerDTOS = bannerService.queryListByCode(bannerReq);
            List<String> covers = new ArrayList<>();
            Optional.ofNullable(bannerDTOS).orElse(new ArrayList<>()).forEach(dto -> {
                covers.add(dto.getBannerUrl());
            });

            HotelHotsH5DTO hotsDTOS = new HotelHotsH5DTO();
            List<HotelHotDTO> hotelHotDTOS = new ArrayList<>();
            for (SearchHit hit : hits) {
                String str = hit.getSourceAsString();
                JSONObject hotJson = JSONObject.parseObject(str);
                HotelRecommendListEs recommendListEs = JSONObject.parseObject(str, HotelRecommendListEs.class);
                HotelHotDTO hotelHotDTO = EntityUtil.copy(recommendListEs, HotelHotDTO.class);
                hotelHotDTOS.add(hotelHotDTO);
            }

            hotsDTOS.setCover(covers);
            hotsDTOS.setTips(tips);
            hotsDTOS.setHotels(hotelHotDTOS);

            PageResponse result = new PageResponse(Arrays.asList(hotsDTOS),
                    true,
                    true,
                    0,
                    (req.getPageNo() - 1) * req.getPageSize(),
                    req.getPageSize());

            // 缓存进redis
            redisTemplate.opsForValue().set(KeyUtil.genKey(RedisConstants.HOTEL_RECOMMEND_TIME, now, req.getPageNo().toString(), req.getPageSize().toString()), JSONObject.toJSONString(result, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullListAsEmpty), 8, TimeUnit.HOURS);

            return result;
        } catch (IOException e) {
            log.error("【业务模块APP服務-查询es产品列表】异常", e);
            throw new BusinessException(SystemError.ES_IO_EXCEPTION_19004);
        }
    }

    @Override
    public List<HotelHotCityDTO> queryRecommendationsPc() {
        // PC端热门酒店写死以下城市
        List<String> citys = Arrays.asList("香港","澳門","深圳","廣州","珠海","上海","北京");
        String now = LocalDate.now().toString();

        Object recommendRedis = redisTemplate.opsForValue().get(KeyUtil.genKey(RedisConstants.HOTEL_RECOMMEND_PC_TIME, now));
        if (recommendRedis != null) {
            List<HotelHotCityDTO> result = JSONArray.parseArray(recommendRedis.toString(), HotelHotCityDTO.class);
            return result;
        }
        // 构建产品检索条件
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConstant.HOTEL_RECOMMEND_PC_INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        sourceBuilder.query(boolQuery);
        sourceBuilder.from(0);
        sourceBuilder.size(10);
        searchRequest.source(sourceBuilder);

        try {
            // 解析响应结果
            SearchResponse response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
            SearchHit[] hits = response.getHits().getHits();
            if (hits.length == 0) {
                initEsRecommendPC(citys);
                response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
                hits = response.getHits().getHits();
            }

            List<HotelHotCityDTO> result = new ArrayList<>();
            for (SearchHit hit : hits) {
                String str = hit.getSourceAsString();
                HotelHotCityDTO hotelHotCityDTO = JSONObject.parseObject(str, HotelHotCityDTO.class);
                result.add(hotelHotCityDTO);
            }

            // 缓存进redis
            redisTemplate.opsForValue().set(KeyUtil.genKey(RedisConstants.HOTEL_RECOMMEND_PC_TIME, now), JSONObject.toJSONString(result, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullListAsEmpty), 8, TimeUnit.HOURS);

            return result;
        } catch (IOException e) {
            throw new BusinessException(SystemError.ES_SEARCH_INFO_19010);
        }

    }

    @Override
    public PageResponse<HotelSearchDTO> search(HotelQueryReq req) {

        // 构建产品检索条件
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConstant.HOTEL_SEARCH_INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        /*boolQuery.should(QueryBuilders.fuzzyQuery("name", req.getSearchName()));
        boolQuery.should(QueryBuilders.fuzzyQuery("enName", req.getSearchName()));
        boolQuery.should(QueryBuilders.fuzzyQuery("cityName", req.getSearchName()));*/
        sourceBuilder.query(boolQuery);
        sourceBuilder.from((req.getPageNo() - 1) * req.getPageSize());
        sourceBuilder.size(req.getPageSize());
        searchRequest.source(sourceBuilder);

        try {
            // 解析响应结果
            SearchResponse response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
            SearchHit[] hits = response.getHits().getHits();
            List <HotelSearchEs> hotelSearchEss = new ArrayList<>();
            if (hits.length == 0) {
                PageResponse<HotelQueryListDTO> hotes = hotelService.queryListH5(req);
                PageResponse<HotelSearchDTO> result = EntityUtil.copy(hotes, PageResponse.class);
                return result;
            }
            for (SearchHit hit : hits) {
                String str = hit.getSourceAsString();
                HotelSearchEs hotelSearchEs = JSONObject.parseObject(str, HotelSearchEs.class);
                hotelSearchEss.add(hotelSearchEs);
            }

            PageResponse result = new PageResponse(hotelSearchEss,
                    true,
                    true,
                    0,
                    (req.getPageNo() - 1) * req.getPageSize(),
                    req.getPageSize());

            return result;
        } catch (IOException e) {
            log.error("【业务模块APP服務-查询es产品列表】异常", e);
            throw new BusinessException(SystemError.ES_IO_EXCEPTION_19004);
        }
    }

    @Override
    public Result<HotelDetailsDTO> detail(HotelDetailsQueryH5Req req) {
        Object detailRedis = redisTemplate.opsForValue().get(KeyUtil.genKey(RedisConstants.HOTEL_DETAIL, req.getId().toString(), req.getCheckInDate().toString(), req.getCheckOutDate().toString()));
        if (detailRedis != null) {
            HotelDetailsDTO result = JSONObject.parseObject(detailRedis.toString(), HotelDetailsDTO.class);
            // 添加足迹
            addUserFootPoint(result,req.getId(), req.getUserId());
            return Result.success(result);
        }
        // 构建产品检索条件
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConstant.HOTEL_DETAIL_INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery
                .must(QueryBuilders.termQuery("id", req.getId()))
                .must(QueryBuilders.termQuery("checkInDate", req.getCheckInDate()))
                .must(QueryBuilders.termQuery("checkOutDate", req.getCheckOutDate()))
                .must(QueryBuilders.termQuery("roomNum", req.getRoomNum()))
                .must(QueryBuilders.termQuery("adultNum", req.getAdultNum()))
                .must(QueryBuilders.termQuery("childrenNum", req.getChildrenNum()));
        sourceBuilder.query(boolQuery);
        searchRequest.source(sourceBuilder);
        try {
            // 解析响应结果
            SearchResponse response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
            SearchHit[] hits = response.getHits().getHits();
            if (hits.length == 0) {
                Result result = initEsDetail(req);
                if (!result.isSuccess()) {
                    return result;
                }
                response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
                hits = response.getHits().getHits();
            }
            HotelDetailsDTO result = null;
            for (SearchHit hit : hits) {
                String str = hit.getSourceAsString();
                HotelDetailEs hotelDetailEs = JSONObject.parseObject(str, HotelDetailEs.class);
                result = EntityUtil.copy(hotelDetailEs, HotelDetailsDTO.class);
            }

            if (null != result) {
                redisTemplate.opsForValue().set(KeyUtil.genKey(RedisConstants.HOTEL_DETAIL, req.getId().toString(), req.getCheckInDate().toString(), req.getCheckOutDate().toString()), JSONObject.toJSONString(result, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullListAsEmpty), 1, TimeUnit.HOURS);
            } else {
                return Result.failed(SystemError.HOTEL_10010);
            }
            // 添加足迹
            addUserFootPoint(result,req.getId(), req.getUserId());
            return Result.success(result);
        } catch (Exception e) {
            log.error("检索信息失败[{}]", e);
            throw new BusinessException(SystemError.ES_SEARCH_INFO_19010);
        }
    }

    @Override
    public HotelRoomDTO detailRoom(RoomDetailsQueryH5Req req) {
        Object detailRedis = redisTemplate.opsForValue().get(KeyUtil.genKey(RedisConstants.HOTEL_DETAIL_ROOM, req.getId().toString(), req.getCheckInDate().toString(), req.getCheckOutDate().toString(), req.getRoomNum().toString()));
        if (detailRedis != null) {
            HotelRoomDTO result = JSONObject.parseObject(detailRedis.toString(), HotelRoomDTO.class);
            return result;
        }
        // 构建产品检索条件
        SearchRequest searchRequest = new SearchRequest(ElasticSearchConstant.HOTEL_ROOM_DETAIL_INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        boolQuery
                .must(QueryBuilders.termQuery("hotelProducts.id", req.getId()))
                .must(QueryBuilders.termQuery("checkInDate", req.getCheckInDate()))
                .must(QueryBuilders.termQuery("checkOutDate", req.getCheckOutDate()))
                .must(QueryBuilders.termQuery("hotelProducts.roomNum", req.getRoomNum()))
                .must(QueryBuilders.termQuery("adultNum", req.getAdultNum()))
                .must(QueryBuilders.termQuery("childrenNum", req.getChildrenNum()));
        sourceBuilder.query(boolQuery);
        searchRequest.source(sourceBuilder);
        try {
            // 解析响应结果
            SearchResponse response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
            SearchHit[] hits = response.getHits().getHits();
            if (hits.length == 0) {
                initEsDetailRoom(req);
                response = restHighLevelClient.search(searchRequest, ElasticSearchConfiguration.COMMON_OPTIONS);
                hits = response.getHits().getHits();
            }
            HotelRoomDTO result = null;
            for (SearchHit hit : hits) {
                String str = hit.getSourceAsString();
                HotelRoomProductEs hotelDetailEs = JSONObject.parseObject(str, HotelRoomProductEs.class);
                result = EntityUtil.copy(hotelDetailEs, HotelRoomDTO.class);
            }

            if (null != result) {
                redisTemplate.opsForValue().set(KeyUtil.genKey(RedisConstants.HOTEL_DETAIL_ROOM, req.getId().toString(), req.getCheckInDate().toString(), req.getCheckOutDate().toString(), req.getRoomNum().toString()), JSONObject.toJSONString(result, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullListAsEmpty), 30, TimeUnit.MINUTES);
            } else {
                throw new BusinessException(SystemError.HOTEL_10014);
            }

            return result;
        } catch (BusinessException e) {
            throw new BusinessException(SystemError.getDefined(e.getCode()));
        } catch (Exception e) {
            log.error("检索信息失败[{}]", e);
            throw new BusinessException(SystemError.ES_SEARCH_INFO_19010);
        }
    }

    // 增加足迹
    private void addUserFootPoint(HotelDetailsDTO details, Long detailId, Long userId) {
        try {
            if (userId != null) {
                UserFootPointReq req = new UserFootPointReq();
                req.setUserId(userId);
                req.setType(SystemBusinessType.HOTEL.getCode());
                req.setTitle(details.getName());
                req.setContent(details.getEnName());
                req.setCurrencyId(Currency.HKD.getCode());
                req.setCurrency(Currency.HKD.getCurrency());
                req.setPrice(details.getHotelRooms().get(0).getHotelProducts().get(0).getPrice());
                String showPhotoJson = details.getShowPhotoJson();
                req.setCoverUrl(JSONArray.parseArray(showPhotoJson).getJSONObject(0).getString("url"));
                req.setDetailId(detailId);
                req.setStarLevel(details.getStarLevel());
                userFootPointService.add(req);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
